package booking

import (
	"car-thirdparty-api/internal/consts"
	"car-thirdparty-api/internal/dao"
	"car-thirdparty-api/internal/model"
	"car-thirdparty-api/internal/model/entity"
	"car-thirdparty-api/library/liberr"
	"context"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/gogf/gf/v2/util/gconv"
	"strings"
	"time"

	"github.com/duke-git/lancet/v2/datetime"
	"github.com/gogf/gf/v2/errors/gerror"
)

func (c *LogicV1) BookingAdd(ctx context.Context, req *model.BookingAddReq) (res *model.BookingAddRes, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		// 最大预约天数判断
		var sysUser *entity.SysUser
		err = dao.SysUser.Ctx(ctx).Where(dao.SysUser.Columns().UserName, req.UserId).Scan(&sysUser)
		if sysUser == nil {
			err = gerror.NewCode(UserNotExistErrCode)
		}
		liberr.ErrIsNil(ctx, err)

		// 查询预约申请人员部门信息
		var bookingLimit *entity.BookingLimit
		err = dao.BookingLimit.Ctx(ctx).Where(dao.BookingLimit.Columns().SysUser, sysUser.UserId).Scan(&bookingLimit)
		if bookingLimit == nil {
			err = gerror.NewCode(NoAuthErrCode)
		}
		liberr.ErrIsNil(ctx, err)

		// 判断预约时间是否在限定的天数范围内
		date1 := strings.Split(req.StartTime, " ")[0]
		orderStartTime := gtime.NewFromStr(date1 + " 00:00:00")
		date1 = strings.Split(req.EndTime, " ")[0]
		orderEndTime := gtime.NewFromStr(date1 + " 23:59:59")
		if orderEndTime.Before(orderStartTime) {
			err = gerror.NewCode(InvalidDateErrCode)
		}
		liberr.ErrIsNil(ctx, err)

		diffDays := datetime.DaysBetween(orderStartTime.Time, orderEndTime.Time)
		if diffDays > bookingLimit.MaxDays {
			err = gerror.NewCode(OverLimitErrCode)
		}
		liberr.ErrIsNil(ctx, err)

		// 查看预约车牌数，是否大约可用预约次数
		carNums := strings.Split(req.CarNum, ",")
		if len(carNums) > bookingLimit.LimitTime {
			err = gerror.NewCode(NoLimitErrCode)
		}
		liberr.ErrIsNil(ctx, err)

		schools := strings.Split(req.SchoolId, ",")
		failCount := 0
		retData := make([]string, 0)
		for _, carNum := range carNums {
			status, err := c.checkBookingStatus(ctx, req, carNum)
			if err != nil {
				failCount++
				retData = append(retData, carNum+"-预约状态检查失败")
				g.Log().Error(ctx, "检查预约可用状态失败，车牌号为:", carNum)
				continue
			}

			switch status.Code {
			case consts.BLACK.Code:
				failCount++
				retData = append(retData, carNum+"-黑名单生效中禁止预约")
				g.Log().Error(ctx, "车牌号[", carNum, "]黑名单生效中，禁止预约")
				continue
			case consts.CARD_ONLINE.Code:
				failCount++
				retData = append(retData, carNum+"-月租车生效中禁止预约")
				g.Log().Error(ctx, "车牌号[", carNum, "]月卡生效中，禁止预约")
				continue
			case consts.CARD.Code:
				failCount++
				retData = append(retData, carNum+"-办证信息生效中禁止预约")
				g.Log().Error(ctx, "车牌号[", carNum, "]办证生效中，禁止预约")
				continue
			case consts.CARD_BOOKING.Code:
				failCount++
				retData = append(retData, carNum+"-预约信息生效中禁止重复预约")
				g.Log().Error(ctx, "车牌号[", carNum, "]预约信息生效中，禁止重复预约")
				continue
			default:
				break
			}

			// 插入预约记录
			bookingCard := &entity.BookingCard{
				CarNum:         carNum,
				CarType:        "YYCL",
				DeptId:         gconv.String(sysUser.DeptId),
				OrderEndTime:   orderEndTime,
				OrderStartTime: orderStartTime,
				OrderInfo:      "访客系统预约",
				CreateBy:       gconv.String(req.UserId),
				//UpdateBy:       gconv.String(sysUser.UserId),
				CreateTime: gtime.Now(),
				//UpdateTime:     gtime.Now(),
				OrderTel:  req.Contact,
				SchoolId:  req.SchoolId,
				OrderUser: gconv.String(sysUser.NickName),
				Flag:      1,
				Status:    0,
			}
			insertId, err := dao.BookingCard.Ctx(ctx).InsertAndGetId(bookingCard)
			if err != nil {
				g.Log().Error(ctx, "插入预约记录失败，车牌号为:", carNum)
				continue
			}

			// 插入预约信息表
			bookingInfos := make([]entity.BookingCardInfo, 0)
			for _, schoolId := range schools {
				bookingInfo := &entity.BookingCardInfo{
					SchoolId: gconv.Int(schoolId),
					OrderId:  int(insertId),
					Flag:     1,
				}
				bookingInfos = append(bookingInfos, *bookingInfo)
			}
			_, err = dao.BookingCardInfo.Ctx(ctx).Insert(bookingInfos)
			if err != nil {
				g.Log().Error(ctx, "插入预约信息记录失败，车牌号为:", carNum)
			}
		}

		// 扣减预约次数限制
		successCount := len(carNums) - failCount
		bookingLimit.LimitTime = bookingLimit.LimitTime - successCount
		_, err := dao.BookingLimit.Ctx(ctx).Where(dao.BookingLimit.Columns().SysUser, sysUser.UserId).Update(bookingLimit)
		if err != nil {
			g.Log().Error(ctx, "更新预约次数失败，用户登录名为", req.UserId)
		}

		if len(retData) == 0 {
			res = &model.BookingAddRes{
				Message: "预约成功",
			}
		} else {
			res = &model.BookingAddRes{
				Message: strings.Join(retData, ","),
			}
		}
	})

	return
}

func (c *LogicV1) checkBookingStatus(ctx context.Context, req *model.BookingAddReq, carNum string) (res *consts.CheckBookingStatus, err error) {
	// 检查黑名单
	var blackList *entity.BlackList
	err = dao.BlackList.Ctx(ctx).
		Where(dao.BlackList.Columns().CarNum+" = ?", carNum).
		Where(dao.BlackList.Columns().Flag+" != ?", 3).
		Limit(1).
		Scan(&blackList)
	if err != nil {
		g.Log().Error(ctx, "获取黑名单失败，车牌号为:", carNum)
		return
	}
	if blackList != nil {
		g.Log().Info(ctx, "黑名单生效中，车牌号为:", carNum)
		res = &consts.BLACK
		return
	}

	startTime := gtime.NewFromStr(req.StartTime)
	endTime := gtime.NewFromStr(req.EndTime)

	// 检查是否存在月租
	var CardOnline *entity.CardOnline
	err = dao.CardOnline.Ctx(ctx).
		Where(dao.CardOnline.Columns().CarNum+" = ?", carNum).
		Where(dao.CardOnline.Columns().Flag+" != ?", 3).
		Limit(1).
		Scan(&CardOnline)
	if err != nil {
		g.Log().Error(ctx, "获取月租车信息失败，车牌号为:", carNum)
		return
	}
	if CardOnline != nil {
		// 预约的开始时间或者结束时间在月卡有效期内
		if c.compareTwoDate(startTime.Time, endTime.Time, CardOnline.StartTime.Time, CardOnline.EndTime.Time) {
			g.Log().Info(ctx, "月租车生效中，车牌号为:", carNum)
			res = &consts.CARD_ONLINE
			return
		}
	}

	// 检查是否存在办卡
	var Card *entity.Card
	err = dao.Card.Ctx(ctx).
		Where(dao.Card.Columns().CarNum+" = ?", carNum).
		Where(dao.Card.Columns().Flag+" != ?", 3).
		Scan(&Card)
	if err != nil {
		g.Log().Error(ctx, "获取办证信息失败，车牌号为:", carNum)
		return
	}
	if Card != nil {
		// 预约的开始时间或者结束时间在办证有效期内
		if c.compareTwoDate(startTime.Time, endTime.Time, Card.StartTime.Time, Card.EndTime.Time) {
			g.Log().Info(ctx, "办证信息生效中，车牌号为:", carNum)
			res = &consts.CARD
			return
		}
	}

	// 检查是否存在有效预约
	var bookingCards []*entity.BookingCard
	err = dao.BookingCard.Ctx(ctx).
		Where(dao.BookingCard.Columns().CarNum+" = ?", carNum).
		Where(dao.BookingCard.Columns().Flag+" != ?", 3).
		Scan(&bookingCards)
	if err != nil {
		g.Log().Error(ctx, "获取预约信息失败，车牌号为:", carNum)
		return
	}
	if bookingCards != nil && len(bookingCards) > 0 {
		for _, bookingCard := range bookingCards {
			bookingSchools := strings.Split(bookingCard.SchoolId, ",")
			for _, school := range bookingSchools {
				if strings.Contains(req.SchoolId, school) {
					if c.compareTwoDate(startTime.Time, endTime.Time, bookingCard.OrderStartTime.Time, bookingCard.OrderEndTime.Time) {
						g.Log().Info(ctx, "同校区预约时间冲突，车牌号为:", carNum)
						res = &consts.CARD_BOOKING
						return
					}
				}
			}
		}
	}

	res = &consts.NORMAL
	return res, nil
}

func (c *LogicV1) compareTwoDate(startOne, endOne, startTwo, endTwo time.Time) bool {
	// 计算两个时间段是否重叠
	return startOne.Before(endTwo) && endOne.After(startTwo)
	//if (!startOne.After(endTwo)) || endOne.Before(startTwo) {
	//	return true
	//}
	//
	//return false
}
